﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WiiGeeDotNet.filter;
using WiiGeeDotNet.logic;
using WiiGeeDotNet.events;

namespace WiiGeeDotNET
{
  public class WiiGeeHandler
  {
    protected static String version = "1.3.1 alpha";
    protected static String releasedate = "20081215";

    protected static WiiGeeHandler instance;
    private static Object lockObject = new Object();

    public delegate void NewGestureDelegate(GestureEvent _event);
    public event NewGestureDelegate NewGestureCreated;
    public event NewGestureDelegate NewGesture;

    public delegate void StateChangedDelegate(StateEvent _event);
    public event StateChangedDelegate StateChanged;

    public delegate void MotionStartedDelegate(MotionStartEvent _event);
    public event MotionStartedDelegate MotionStarted;

    public delegate void MotionStoppedDelegate(MotionStopEvent _event);
    public event MotionStoppedDelegate MotionStopped;

    protected List<Filter> m_filters = new List<Filter>();
    protected TriggeredProcessingUnit m_analyzer;

    private bool m_learning = false;
    private bool m_analyzing = false;

    /// <summary>
    /// The engine is currently analizing a gesture
    /// </summary>
    public bool Analyzing
    {
      get { return m_analyzing; }
      set { m_analyzing = value; }
    }

    /// <summary>
    /// The engine is currently learning a new gesture
    /// </summary>
    public bool Learning
    {
      get { return m_learning; }
      set { m_learning = value; }
    }

    /// <summary>
    /// All availbable gesture models
    /// </summary>
    public Dictionary<int, GestureModel> GestureModels
    {
      get { return m_analyzer.GestureModels; }
    }

    /// <summary>
    /// Constructor
    /// </summary>
    private WiiGeeHandler()
    {
      Console.WriteLine("This is wiigee version " + version + " (" + releasedate + ")");

      m_analyzer = new TriggeredProcessingUnit();
      m_analyzer.NewGesture += new ProcessingUnit.NewGestureDelegate(m_analyzer_NewGesture);
      m_analyzer.StateChanged += new ProcessingUnit.StateChangedDelegate(m_analyzer_StateChanged);
      MotionDetectFilter mdFilter = new MotionDetectFilter();
      mdFilter.MotionStarted += new MotionDetectFilter.MotionStartedDelegate(mdFilter_MotionStarted);
      mdFilter.MotionStopped += new MotionDetectFilter.MotionStoppedDelegate(mdFilter_MotionStopped);

      this.AddFilter(new IdleStateFilter());
      this.AddFilter(mdFilter);
      this.AddFilter(new DirectionalEquivalenceFilter());

    }

    void mdFilter_MotionStopped(MotionStopEvent _event)
    {
      //Console.WriteLine("Motion Stopped");
      if (MotionStopped != null)
      {
        MotionStopped(_event);
      }
    }

    void mdFilter_MotionStarted(MotionStartEvent _event)
    {
      //Console.WriteLine("Motion Started");

      if (MotionStarted != null)
      {
        MotionStarted(_event);
      }
    }

    void m_analyzer_StateChanged(StateEvent _event)
    {
      if (StateChanged != null)
      {
        StateChanged(_event);
      }

    }

    void m_analyzer_NewGesture(GestureEvent _event)
    {
      if (NewGesture != null)
      {
        NewGesture(_event);
      }
    }

    /// <summary>
    /// Returns an instance of the engine (only one engine allowed running at a time)
    /// </summary>
    /// <returns></returns>
    public static WiiGeeHandler GetInstance()
    {
      if (instance == null)
      {
        instance = new WiiGeeHandler();
        return instance;
      }
      else
      {
        return instance;
      }
    }

    /// <summary>
    /// Adds acceleration values to the engine
    /// </summary>
    /// <param name="_x">x value of acceleration</param>
    /// <param name="_y">y value of acceleration</param>
    /// <param name="_z">z value of acceleration</param>
    public void AddAccelerationValue(double _x, double _y, double _z)
    {
      double[] accel = new double[3] { _x, _y, _z };
      for (int i = 0; i < m_filters.Count; i++)
      {
        accel = m_filters[i].FilterVector(accel);
        // cannot return here if null, because of time-dependent filters
      }

      if (accel != null)
      {
        double absvalue = Math.Sqrt((accel[0] * accel[0]) + (accel[1] * accel[1]) + (accel[2] * accel[2]));
        AccelerationEvent w = new AccelerationEvent(accel[0], accel[1], accel[2], absvalue);
        m_analyzer.AccelerationReceived(w);
      }
    }

    public void BeginAnalyzing()
    {
      m_analyzer.StartAnalyzing();
    }

    public void EndAnalyzing()
    {
      m_analyzer.StopAnalyzing();
    }

    public void BeginTrainGesture()
    {
      m_analyzer.StartLearning();
    }

    public void EndTrainGesture()
    {
      m_analyzer.StopLearning();
    }

    public int CommitGesture(String _name)
    {
      int id = m_analyzer.FinishTraining(_name);
      if (NewGestureCreated != null)
      {//a new gesture has been trained
        NewGestureCreated(new GestureEvent(id, _name));
      }
      return id;
    }

    public void DiscardTrainingData()
    {
      m_analyzer.FinishTraining("", false);
    }

    public String GetGestureData(int _id)
    {
      return m_analyzer.GetGestureData(_id);
    }

    public int AddGesture(String _data)
    {
      return m_analyzer.AddGesture(_data);
    }

    public void RemoveGesture(int _id)
    {
      m_analyzer.RemoveGesture(_id);
    }

    public List<Gesture> LearningSequence
    {
      get
      {
        return m_analyzer.Trainsequence;
      }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="filter"></param>
    private void AddFilter(Filter filter)
    {
      this.m_filters.Add(filter);
      Console.WriteLine("Filter added...");
    }
  }
}
